/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cjf.aop.runtime.utils;

import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;

import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;

/**
 * 应用的全局线程池 （包括单线程池的磁盘io，多线程池的网络io和主线程）
 */
public final class AppExecutors {
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

    private static AppExecutors sInstance;

    /**
     * 单线程池
     */
    private final ExecutorService mSingleIO;

    /**
     * 多线程池
     */
    private ExecutorService mPoolIO;

    /**
     * 主线程
     */
    private final MainThreadExecutor mMainThread;

    private AppExecutors(ExecutorService singleIO, ExecutorService poolIO, MainThreadExecutor mainThread) {
        mSingleIO = singleIO;
        mPoolIO = poolIO;
        mMainThread = mainThread;
    }

    /**
     * 更新多线程池
     *
     * @param nThreads 线程池线程的数量
     * @return
     */
    public AppExecutors updatePoolIO(int nThreads) {
        mPoolIO = Executors.newFixedThreadPool(nThreads);
        return this;
    }

    private AppExecutors() {
        this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(CORE_POOL_SIZE),
                new MainThreadExecutor());
    }

    /**
     * 获取线程管理实例
     *
     * @return
     */
    public static AppExecutors get() {
        if (sInstance == null) {
            synchronized (AppExecutors.class) {
                if (sInstance == null) {
                    sInstance = new AppExecutors();
                }
            }
        }
        return sInstance;
    }

    public ExecutorService singleIO() {
        return mSingleIO;
    }

    public ExecutorService poolIO() {
        return mPoolIO;
    }

    public MainThreadExecutor mainThread() {
        return mMainThread;
    }

    public static class MainThreadExecutor implements Executor {
        private Handler mainThreadHandler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
            mainThreadHandler.post(command);
        }

        /**
         * 这是一个可以取消的异步计算，该类提供了Future的基本实现，具有启动和取消运算，查询运算是否结束，
         * 并且检查返回计算的结果，该结果只能在运行完成之后才能获取到，如果程序没有运行结束，则`get()`将会阻塞。
         * 程序运行结束之后，无法重新启动或者是取消程序（除非调用`runAndReset`方法）
         * 当程序里面的FutureTask未执行完成的时候get()方法会一直阻塞调用该方法的线程，
         * 直到FutureTask里面的任务执行才会解除阻塞。所以get()方法是一个阻塞式的去获取结果的
         * https://blog.csdn.net/u012417984/article/details/72929864
         * https://www.jianshu.com/p/9eb1263e27df
         * @param task
         * @param <T>
         * @return
         */
        public <T> Future<T> submit(Callable<T> task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<T> ftask = new FutureTask<>(task);
            execute(ftask);
            return ftask;
        }
    }
}
